#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <vector>              // 用于存储工作线程
#include <queue>               // 用于实现任务队列
#include <memory>              // 智能指针
#include <thread>              // C++11线程库
#include <mutex>               // 互斥锁，保证线程安全
#include <condition_variable>  // 条件变量，用于线程通信
#include <future>              // 异步任务结果
#include <functional>          // std::function，用于封装任务
#include <stdexcept>           // 标准异常
#include <atomic>              // 原子操作，线程安全的变量

class ThreadPool {
public:
    // 构造函数，创建指定数量的工作线程
    ThreadPool(size_t threads);
    
    // 禁用拷贝构造函数和赋值操作符
    ThreadPool(const ThreadPool&) = delete;
    ThreadPool& operator=(const ThreadPool&) = delete;
    
    // 析构函数
    ~ThreadPool();

    // 判断线程池是否停止工作
    bool isStopped();

    // 提交任务（模板不支持分离编译）
    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args)->std::future<typename std::invoke_result<F, Args...>::type>;

private:
    // 工作线程 - 线程池的核心逻辑
    void workerThread();

private:
    // 工作线程容器
    std::vector<std::thread> workers;
    
    // 任务队列
    std::queue<std::function<void()>> tasks;
    
    // 同步机制
    std::mutex queue_mutex;
    std::condition_variable condition;
    
    // 控制线程池停止
    std::atomic<bool> stop{false};
};


template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)->std::future<typename std::invoke_result<F, Args...>::type>
{
    // 一、获取任务的返回类型
    using return_type = typename std::invoke_result<F, Args...>::type;

    // 二、将任务包装成 无参无返 的类型
    /**
     * 简介: std::packaged_task 是C++11引入的一个类模板
     * 
     * 语法：std::packaged_task<ReturnType(ArgTypes...)>
     * std::packaged_task的模板参数必须为完整的函数签名
     * 
     * 功能: 包装一个可调用对象，并将其执行结果与一个std::future对象关联起来，使得异步获取任务执行结果变得简单。
     * 1.它包装了一个可调用对象
     * 2.当调用packaged_task对象时，实际上是在调用被包装的可调用对象
     * 3.它自动将调用结果存储在一个共享状态中
     * 4.通过get_future()方法可以获取一个future对象，用于异步获取结果
     * 
     * 注意:
     * 1.packaged_task是不可拷贝的，但可以移动
     * 2.get_future()只能调用一次，多次调用会抛出std::future_error
     * 3.如果packaged_task被销毁而关联的future还未获取结果，则future会抛出异常
     * 4.与std::async相比，packaged_task提供了更多的控制权，但需要手动管理线程
     * 
     * 拓展:
     * 1. std::packaged_task 和 std::bind 的返回对象都是 只能移动（move-only） 的类型:
     * std::bind 返回的是一个未指定类型的可调用对象（通常是一个内部绑定器类型），它 不能拷贝，但可以移动。
     * std::packaged_task 也是不可拷贝的（它的拷贝构造函数和拷贝赋值运算符被删除），只能移动。
     * 2. std::move 的作用:
     * std::move(bindedFunc) 将 bindedFunc 转换成一个右值引用，使得它可以被移动构造或移动赋值，而不是尝试拷贝（会导致编译错误）。
     */
    // auto task = std::make_shared<std::packaged_task<return_type()>>(
    // std::bind(std::forward<F>(f), std::forward<Args>(args)...)
    // );
    // 下面这三行代码效果同上!!!
    auto bindedFunc = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
    std::packaged_task<return_type()> packagedTask(std::move(bindedFunc));
    auto taskPtr = std::make_shared<std::packaged_task<return_type()>>(std::move(packagedTask));

    // 三、获取保存任务异步处理结果的future
    std::future<return_type> result = taskPtr->get_future();

    // 四、将任务提交到队列中(加锁保护)
    {
        // 1.加锁保护
        std::unique_lock<std::mutex> lock(this->queue_mutex);
        // 2.检查线程池是否停止
        if(this->stop){
            throw std::runtime_error("enqueue on stoped ThreadPool");
        }
        // 3.线程池未停止，提交任务
        this->tasks.emplace([taskPtr](){
            (*taskPtr)();
        });
        // 4.唤醒正在等待的工作线程
        this->condition.notify_one();
    }
        // 五、返回future，让调用者能够获取异步返回的结果
    return result;
}

#endif // THREAD_POOL_H